﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;


using ComLib;
using ComLib.Entities;
using ComLib.Authentication;



namespace CommonLibraryNet.Web.Lib
{
    /// <summary>
    /// Base class for controllers.
    /// </summary>
    public class ControllerWithAutomaticDataBinding<T> : ControllerBase<T> where T : class, IEntity, new()
    {
        /// <summary>
        /// Create the entity and persist it.
        /// </summary>
        /// <param name="job"></param>
        /// <returns></returns>
        [AcceptVerbs(HttpVerbs.Post), Authorize]        
        public virtual ActionResult Create(T entity)
        {
            return Create<T>(entity, "Details");
        } 
    }



    /// <summary>
    /// Base class for controllers.
    /// </summary>
    public class ControllerManualDataBinding<T> : ControllerBase<T> where T : class, IEntity, new()
    {
        /// <summary>
        /// Create the entity and persist it.
        /// </summary>
        /// <param name="job"></param>
        /// <returns></returns>
        [AcceptVerbs(HttpVerbs.Post), Authorize]
        public virtual ActionResult Create(FormCollection form)
        {
            T entity = new T();
            UpdateModel(entity);
            return Create<T>(entity, "Details");
        }
    }



    /// <summary>
    /// Base class for controllers.
    /// </summary>
    public class ControllerBase<T> : Controller, CommonLibraryNet.Web.Lib.IEntityController where T : class, IEntity, new()
    {

        #region Mvc Controller methods
        /// <summary>
        /// Create a new entity.
        /// </summary>
        /// <returns></returns>
        public virtual ActionResult Create()
        {
            return View(new T());
        }


        /// <summary>
        /// Edit the entity referenced by id.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [Authorize]        
        public virtual ActionResult Edit(int id)
        {
            IEntityService<T> service = EntityRegistration.GetService<T>();
            IActionContext ctx = EntityRegistration.GetContext<T>(id);
            BoolResult<T> fetchResult = service.Get(ctx);

            if (fetchResult.Item == null)
                return View("NotFound");

            return View(fetchResult.Item);
        }


        /// <summary>
        /// Edit the entity referenced by id.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="collection"></param>
        /// <returns></returns>
        [AcceptVerbs(HttpVerbs.Post)]
        public virtual ActionResult Edit(int id, FormCollection collection)
        {
            return Edit<T>(id, "Details");
        }        


        /// <summary>
        /// Show the details of the entity.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual ActionResult Details(int id)
        {
            IEntityService<T> service = EntityRegistration.GetService<T>();
            IActionContext ctx = EntityRegistration.GetContext<T>(id);
            BoolResult<T> result = service.Get(ctx);
            return View(result.Item);
        }


        /// <summary>
        /// Delete the entity with the id.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [Authorize]
        public virtual ActionResult Delete(int id)
        {
            return Delete<T>(id, string.Empty);
        }


        /// <summary>
        /// Delete the entity with confirmation.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="confirmButton"></param>
        /// <returns></returns>
        [AcceptVerbs(HttpVerbs.Post), Authorize]
        public virtual ActionResult Delete(int id, string confirmButton)
        {
            return Delete<T>(id, confirmButton);
        }


        /// <summary>
        /// Get index of entities ( based on page ).
        /// </summary>
        /// <param name="page"></param>
        /// <returns></returns>   
        public virtual ActionResult Index(int? page)
        {
            IEntityService<T> service = EntityRegistration.GetService<T>();
            IActionContext ctx = EntityRegistration.GetContext<T>();
            int pageSize = 10;
            int pageIndex = page == null ? 1 : (int)page;
            BoolResult<PagedList<T>> result = service.Get(pageIndex, pageSize);
            // pagedData.Url = "/" + typeof(T).Name + "s/index?";
            ActionResult action = View("List", result.Item);
            return action;
        }


        /// <summary>
        /// Edit the entity referenced by id.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="collection"></param>
        /// <returns></returns>
        [Authorize]        
        public virtual ActionResult Manage(FormCollection formData)
        {
            IEntityService<T> service = ActiveRecordRegistration.GetService<T>();
            IActionContext ctx = ActiveRecordRegistration.GetContext<T>();
            ctx.UserName = Auth.UserShortName;

            int pageIndex = GetPage("pagenum");
            int pageSize = 10;
            BoolResult<PagedList<T>> result = service.GetByUser(Auth.UserShortName, pageIndex, pageSize);
            //pagedData.Url = "/" + typeof(T).Name + "s/manage?";
            ActionResult action = View(result.Item);
            return action;
        }
        #endregion


        #region Utility Methods
        /// <summary>
        /// Performs an entity create/edit action.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity">The entity being persisted.</param>
        /// <param name="executor">The lambda to persist the entity.</param>
        /// <param name="successUrlId">Url identitfier e.g. "Details"</param>
        /// <returns></returns>
        protected virtual ActionResult Create<T>(IEntity entity, string successUrlId)
        {
            ActionResult result = null;
            BoolResult<T> saveResult = BoolResult<T>.False;
            try
            {
                IEntityService<T> service = ActiveRecordRegistration.GetService<T>();
                IActionContext ctx = ActiveRecordRegistration.GetContext<T>(entity);

                // Save the entity.
                saveResult = service.Create(ctx);

                // Successful ?
                if (saveResult.Success)
                {
                    result = RedirectToAction(successUrlId, new { id = entity.Id });
                }
                else
                {
                    //ModelStateHelpers.AddModelErrors(ModelState, saveResult.Errors);
                    result = View(entity);
                }
            }
            catch
            {
                ModelState.AddModelError("", "Unexpected error occurred saving " + typeof(T).Name);
                result = View(entity);
            }
            return result;
        }


        /// <summary>
        /// Edit the entity by passing in the form collection so it maps the 
        /// html fields to the entity properites.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id"></param>
        /// <param name="collection"></param>
        /// <param name="fetcher"></param>
        /// <returns></returns>
        protected virtual ActionResult Edit<T>(int id, string successUrlId)
            where T : class, IEntity
        {
            T entity = default(T);

            try
            {
                IEntityService<T> service = ActiveRecordRegistration.GetService<T>();
                IActionContext ctx = ActiveRecordRegistration.GetContext<T>(id);
            
                BoolResult<T> result = service.Get(ctx);

                // Check if obtained.
                if (!result.Success) return View("Unable to retrieve post");

                entity = result.Item;
                UpdateModel(entity);
                
                // Persist the edit
                ctx.Item = entity;
                result = service.Update(ctx);

                if (result.Success)
                {
                    return RedirectToAction(successUrlId, new { id = entity.Id });
                }
                else
                {
                    //ModelStateHelpers.AddModelErrors(ModelState, result.Errors);
                    return View(entity);
                }
            }
            catch
            {
                return View("Unexpected error while editing " + typeof(T).Name);
            }
        }


        /// <summary>
        /// Delete the entity.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fetcher"></param>
        /// <param name="deletor"></param>
        /// <param name="id"></param>
        /// <param name="confirmButton"></param>
        /// <returns></returns>
        protected virtual ActionResult Delete<T>(int id, string confirmButton)
        {
            IEntityService<T> service = ActiveRecordRegistration.GetService<T>();
            IActionContext ctx = ActiveRecordRegistration.GetContext<T>(id);
            
            // Get the entity.
            BoolResult<T> fetchResult = service.Get(ctx);

            // If not found.
            if (fetchResult.Item == null) return View("NotFound");

            // Try deleting.
            ctx.Item = fetchResult.Item;
            BoolResult<T> deleteResult = service.Delete(ctx);

            // Deleted.
            if( deleteResult.Success ) return View("Deleted");

            return View("Error saving");        
        }

        
        /// <summary>
        /// Get the current page used for the index and manage page views.
        /// </summary>
        /// <param name="pageParamName"></param>
        /// <returns></returns>
        protected virtual int GetPage(string pageParamName)
        {
            string pageStr = this.Request.QueryString[pageParamName];
            int pageIndex = 1;
            if (!string.IsNullOrEmpty(pageStr))
            {
                int.TryParse(pageStr, out pageIndex);
            }
            return pageIndex;
        }
        #endregion
    }
}
